#include <iostream>
#include <queue>
#include <pthread.h>
#include <unistd.h>

using namespace std;

template <class T>
class BlockQueue { 
public:
    BlockQueue(int max_size = 10) {
        max_size_ = max_size;
        pthread_mutex_init(&mutex_, NULL);
        pthread_cond_init(&c_cond_, NULL);
        pthread_cond_init(&p_cond_, NULL);
    }

    ~BlockQueue() {
        pthread_mutex_destroy(&mutex_);
        pthread_cond_destroy(&c_cond_);
        pthread_cond_destroy(&p_cond_);
    }


    void push(const T& in) {
        pthread_mutex_lock(&mutex_);  // 先加锁
        while (queue_.size() == max_size_){  // 队列已满
            pthread_cond_wait(&p_cond_, &mutex_);   // 阻塞，并且自动释放锁
        }

        // (当被唤醒) 或者 (队列没满，且未在阻塞状态)
        queue_.push(in);
        pthread_cond_signal(&c_cond_);  // 生产者生产了一次，所以可以唤醒一个消费者
        pthread_mutex_unlock(&mutex_);
    }

    const T pop(){
        pthread_mutex_lock(&mutex_);
        while (queue_.empty()){
            pthread_cond_wait(&c_cond_, &mutex_);
        }

        // (当被唤醒) 或者 (队列不为空，未在阻塞状态)
        T res = queue_.front();  // 先获取队首元素
        queue_.pop();
        pthread_cond_signal(&p_cond_);  // 消费者消费了一次，所以可以唤醒一个生产者
        pthread_mutex_unlock(&mutex_);
        return res;
    }


private:
    std::queue<T> queue_;   
    int max_size_;
    pthread_mutex_t mutex_;   // 临界资源的数据结构必须包含锁属性   这个锁可以是构造函数创建的 也可以是外部传入的
    pthread_cond_t c_cond_;     // 临界资源的数据结构必须包含条件属性    消费
    pthread_cond_t p_cond_;     // 生产
};